/**
 * The contents of this file are subject to the license and copyright detailed in the LICENSE and NOTICE files at the root of the source tree and available online at
 * http://www.askosi.org/license.html
 */
package be.destin.skos.core;

import java.util.LinkedList;
import java.util.TreeMap;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;

import org.apache.log4j.Logger;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.repository.object.annotations.iri;

import be.destin.beans.Reflector;
import be.destin.rdf.changes.SkosStatement;
import be.destin.rdf.changes.Status;
import be.destin.rdf.changes.StatusInterface;
import be.destin.skos.html.LocalError;
import be.destin.skos.html.SkosHtmlManager;
import be.destin.skos.search.CountCacher;
import be.destin.util.Util;



/**
 * @author Christophe Dupriez
 * 
 * TODO: Dates de modification: dcterms:created, dcterms:issued, dcterms:modified
 * 
 */

@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlType(propOrder={"prefLabel","altLabel","hiddenLabel","changeNote","definition","editorialNote","example","historyNote","scopeNote","internalNote","exactMatch","closeMatch","broader","related","broadMatch","narrowMatch","relatedMatch","alias","notation","collection", "usage","narrowerUsage","url","icon","status"})
@iri(SchemeInterface.skosNamespace+"Concept")
public class Concept implements Comparable<Concept>, URI, StatusInterface {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/** log4j category */
	private static Logger log = Logger.getLogger(Concept.class);

	public static final int maxDepth = 30;
	public static final String tagIndexedItem /* for RDF */ = "zbwext:indexedItem"; //"askosi:isSubjectOf";
	public static final String SESAME_NAMESPACE = "Concept:";

	private String about;
	protected ConceptScheme inScheme;
	private long sortKey = 0;

	private TermList prefLabel;
	private TermList altLabel;
	private TermList hiddenLabel;

	//skos:note, skos:changeNote, skos:definition, skos:editorialNote, skos:example, skos:historyNote and skos:scopeNote are each instances of owl:AnnotationProperty.
	//skos:changeNote, skos:definition, skos:editorialNote, skos:example, skos:historyNote and skos:scopeNote are each sub-properties of skos:note.
	private TermList changeNote;
	private TermList definition;
	private TermList editorialNote;
	private TermList example;
	private TermList historyNote;
	private TermList scopeNote;
	private LinkedList<LocalLinkedConcept> aliasses;
	private LinkedList<Notation> notation;
	/*
skos:semanticRelation
 |
 +- skos:related
 |   |
 |   +- skos:relatedMatch
 |
 +- skos:broaderTransitive
 |   |
 |   +- skos:broader
 |       |
 |       +- skos:broadMatch
 |
 +- skos:narrowerTransitive
 |   |
 |   +- skos:narrower
 |       |
 |       +- skos:narrowMatch
 |
 +- skos:mappingRelation
     |
     +- skos:closeMatch
     |   |
     |   +- skos:exactMatch
     |
     +- skos:relatedMatch
     |
     +- skos:broadMatch
     |
     +- skos:narrowMatch 
    */
	private LinkedList<LinkedConcept> exactMatch;
	private LinkedList<LinkedConcept> closeMatch;
	private LinkedList<LocalLinkedConcept> broader;
	private LinkedList<CollectionScheme> collections;
	@XmlTransient
	private LinkedList<LocalLinkedConcept> narrower;
	private LinkedList<LocalLinkedConcept> related;

	private LinkedList<LinkedConcept> broadMatch;
	@XmlTransient
	private LinkedList<LinkedConcept> narrowMatch;
	private LinkedList<LinkedConcept> relatedMatch;

	private ReferringApplicationList usages;
	private ReferringApplicationList narrowerUsage;
	// total count is simply the total of usages for all applications
	
	// aggregated narrower total count is simply the total of usages for all
	// applications
	
	String internalNote;
    private UrlList urls;
    private UrlList icons;
    
    protected int depth = 0; // Depth of the concept in a hierarchy

    /* Support for Update Workflow: (from http://metadataregistry.org/concept/list/vocabulary_id/31.html)
     * Confirmed: new-proposed, new-under review, published, change-proposed, change-under review, deprecate-proposed, deprecate-under review, deprecated, not approved
             This information is the only one "preloaded" with the concept: the other one are derived from a lazily accessed "log":
     * Log: date-time x user id. x proposed+under review+confirmed x reason for change
     * Date-time
     * Responsible user id.
     */
    private Status status; // SKOS Vocabulary proposed: http://metadataregistry.org/uri/RegStatus

    private LocalError errors;
    
	@XmlTransient
    private boolean complete = true;

	public static final Status[][] transitions = {
		/* no status */			{ Status.New_Proposed },
		/* New_Proposed */		{ Status.New_Under_Review, Status.Deprecated }, // Deprecated for removal if not used anywhere
		/* New_Under_Review */	{ Status.Published, Status.Deprecated },
		/* Published */			{ Status.Change_Proposed, Status.Deprecate_Proposed }, 
		/* Change_Proposed */	{ Status.Change_Under_Review }, // abandon by proposer or rejection by manager
		/* Change_Under_Review*/{ Status.Published }, // rejection by reviewers
		/* Deprecate_Proposed */{ Status.Deprecate_Under_Review }, // abandon by proposer or rejection by manager
		/* Deprecate_Under_Review*/{ Status.Deprecated, Status.Published }, // rejection by reviewers
		/* Deprecated */		{ Status.New_Proposed }, // Not_Approved this status is put automatically when all relations to the concept has been removed 
		/* Not_Approved */		{ Status.New_Proposed }
	};

    public Concept() {
    	super();
    }

    public Concept(ConceptScheme list) {
    	super();
    	this.inScheme = list;
    }

    public static Concept ensure(ConceptScheme list, String about) {
    	Concept existingConcept = list.getConcept(about);
    	if (existingConcept != null) {
    		return existingConcept;
    	} else {
    		return new Concept(list);
    	}
    }

	public Concept getThis() {
		return this;
	}

	/**
	 * This function returns the counters for an application (given in parameter).
	 * A filter (URL string) may be specified if an drill-down search is possible.
	 * @return the HTML presentation of the numbers with links to search
	 */
	public String toHtmlApplicationUsage(SkosHtmlManager skosHtmlManager, String application, String userLanguage) {
		if (application == null) application = skosHtmlManager.getApplication();
		ReferringApplication aReference = this.getApplicationUsage(application);
		if (aReference == null) return "";
		CountList counts = aReference.getCount();
		if (counts == null || counts.size() == 0) return "";
		String applicationRoot = skosHtmlManager.getApplicationRoot(application);
		StringBuffer output = new StringBuffer();
		Object[] beans = new Object[2];
		beans[0] = this;
		int precCount = 0;
		for (Count aCount : counts) {
			int currCount = aCount.getCount();
			if (currCount <= 0) continue;
			output.append(" ");
			beans[1] = aCount;
			String searchURL = null;
			SchemeUsage su = inScheme.getApplicationUsage(application, aCount.getRole() );
			if (su != null) {
				searchURL = su.getSearchURL();
			}
			if (searchURL != null) {
				output.append( "<a href=\"");
				output.append(skosHtmlManager.addFraming(Reflector.substituteURL(applicationRoot,beans,searchURL,userLanguage)));
				output.append("\" target=\"");
				output.append(application);
				output.append("_list\" class=\"authorityReferenceTotal\">");
			}
			output.append(skosHtmlManager.toHtmlRole(application, aCount.getRole(),this));
			if (currCount > 1) {
				if (currCount == precCount) output.append('=');
				else output.append(currCount);
			}
			precCount = currCount;
			String category = aCount.getCategory();
			output.append(skosHtmlManager.toHtmlCategory(application, category, aCount.isPure(), this));
			if (searchURL != null) {
				output.append("</a>");
			}
/*
			if (filterURL != null && filterURL.length() > 0) {
				output.append("<a href=\"");
				output.append(skosHtmlManager.addFraming(Reflector.substituteURL(applicationRoot,beans,filterURL,userLanguage)));
				output.append("\" target=\"");
				output.append(application);
				output.append("_list\" class=\"authorityReferenceFilter\"> </a>"); 
			}
 */
		}
		return output.toString();
	}

	/**
	 * This function returns the counters for an application (given in parameter).
	 * A filter (URL string) may be specified if an drill-down search is possible.
	 * @return the HTML presentation of the numbers with links to search
	 */
	public String toRdfApplicationUsage(SkosHtmlManager skosHtmlManager, String application) {
		if (application == null) application = skosHtmlManager.getApplication();
		ReferringApplication aReference = this.getApplicationUsage(application);
		if (aReference == null) return "";
		CountList counts = aReference.getCount();
		if (counts == null || counts.size() == 0 || aReference.getTotal() == 0) return "";
		String applicationRoot = skosHtmlManager.getApplicationRoot(application);
		StringBuffer output = new StringBuffer();
		Object[] beans = new Object[2];
		beans[0] = this;
		for (Count aCount : counts) {
	        output.append('<');
	        output.append(tagIndexedItem);
	        output.append("><rdf:Description rdf:about=\"");
	        //TODO:best URL: HTML or Atom/XML ???
			String category = aCount.getCategory();
			String searchURL = null;
			SchemeUsage su = inScheme.getApplicationUsage(application, aCount.getRole() );
			if (su != null) {
				searchURL = su.getSearchURL();
			}
			if (searchURL != null) {
				beans[1] = aCount;
				output.append(Reflector.substituteURL(applicationRoot,beans,searchURL,null));
			} else { // Some dummy URL must be built
				output.append('#');
				output.append(this.getAbout());
				output.append('-');
				output.append(application);
				output.append('-');
				output.append(aCount.getRole());
			}
			output.append("\"><dct:requires>");
			output.append(application);
			output.append("</dct:requires>");
			output.append("<dct:isPartOf>");
			output.append(aCount.getRole());
			output.append("</dct:isPartOf>");
			output.append("<dct:extent>");
			output.append(aCount.getCount());
			output.append("</dct:extent>");
			if (category != null) {
				output.append("<dct:type>");
				output.append(category);
				if ( ! aCount.isPure()) output.append('?');
				output.append("</dct:type>");
			}
			output.append("<dct:subject rdf:resource=\"#");
			output.append(this.getAbout());
			output.append("\"/>");
			output.append("</rdf:Description></");
			output.append(tagIndexedItem);
			output.append('>');
		}
		return output.toString();
	}

	/**
	 * @return the about
	 */
    @XmlAttribute(required = true)
       @XmlID
	public String getAbout() {
		return about;
	}
    
	/**
	 * @return the aliasses
	 */
	public LinkedList<LocalLinkedConcept> getAlias() {
		if (this.aliasses == null) this.aliasses = new LinkedList<LocalLinkedConcept>();
		return this.aliasses;
	}
	
	@XmlTransient
	@iri(SchemeInterface.skosNamespace+"alias")
	public LinkedList<String> getAboutAlias() {
		return LocalLinkedConcept.getAbout(this.getAlias());
	}

	@XmlTransient
	public String getAboutScheme() {
		if (inScheme == null) return null;
		return inScheme.getAbout();
	}

	/**
	 * @return the altLabel
	 */
	@iri(SchemeInterface.skosNamespace+"altLabel")
	public TermList getAltLabel() {
		return altLabel;
	}

	/**
	 * @param language
	 * @return the altLabel for this language
	 */
	public TermList getAltLabel(String language) {
		if (this.altLabel == null) return null;
		return this.altLabel.getAllLing(language);
	}

	@iri(SchemeInterface.skosNamespace+"hiddenLabel")
	public TermList getHiddenLabel() {
		return hiddenLabel;
	}

	/**
	 * @param language
	 * @return the altLabel for this language
	 */
	public TermList getHiddenLabel(String language) {
		if (this.hiddenLabel == null) return null;
		return this.hiddenLabel.getAllLing(language);
	}

	/**
	 * @param application
	 * @return the narrowerUsage for this application
	 */
	public ReferringApplication getApplicationNarrowerUsage(String application) {
		if (!complete) this.getInScheme().complete(this);
		if (narrowerUsage == null) return null;
		return narrowerUsage.getReferringApplication(application);
	}

	/**
	 * @param application
	 * @return the usages for the application
	 */
	public ReferringApplication getApplicationUsage(String application) {
		ReferringApplicationList theUsages = this.getUsage();
		return theUsages.getReferringApplication(application);
	}

	/**
	 * @return the exact Matches
	 */
	public LinkedList<LinkedConcept> getExactMatch() {
		if (!complete) this.getInScheme().complete(this);
		if (this.exactMatch == null) this.exactMatch = new LinkedList<LinkedConcept>();
		return this.exactMatch;
	}

	/**
	 * @return the broader Matches
	 */
	public LinkedList<LinkedConcept> getCloseMatch() {
		if (!complete) this.getInScheme().complete(this);
		if (this.closeMatch == null) this.closeMatch = new LinkedList<LinkedConcept>();
		return this.closeMatch;
	}

	/**
	 * @return the broader
	 */
	public LinkedList<LocalLinkedConcept> getBroader() {
		if (!complete) this.getInScheme().complete(this);
		if (this.broader == null) this.broader = new LinkedList<LocalLinkedConcept>();
		return this.broader;
	}

	/**
	 * @return the broader Matches
	 */
	public LinkedList<LinkedConcept> getBroadMatch() {
		if (!complete) this.getInScheme().complete(this);
		if (this.broadMatch == null) this.broadMatch = new LinkedList<LinkedConcept>();
		return this.broadMatch;
	}

	/**
	 * @return the exact Matches
	 */
	@XmlTransient
	@iri(SchemeInterface.skosNamespace+"exactMatch")
	public LinkedList<String> getScheme_AboutExactMatch() {
		return LinkedConcept.getScheme_About(this.getExactMatch());
	}

	/**
	 * @return the broader Matches
	 */
	@XmlTransient
	@iri(SchemeInterface.skosNamespace+"closeMatch")
	public LinkedList<String> getScheme_AboutCloseMatch() {
		return LinkedConcept.getScheme_About(this.getCloseMatch());
	}

	/**
	 * @return the broader
	 */
	@XmlTransient
	@iri(SchemeInterface.skosNamespace+"broader")
	public LinkedList<String> getAboutBroader() {
		return LocalLinkedConcept.getAbout(this.getBroader());
	}

	/**
	 * @return the broader Matches
	 */
	@XmlTransient
	@iri(SchemeInterface.skosNamespace+"broadMatch")
	public LinkedList<String> getScheme_AboutBroadMatch() {
		return LocalLinkedConcept.getScheme_About(this.getBroadMatch());
	}

	/**
	 * @return the broader Matches
	 */
	public LinkedList<LinkedConcept> getNarrowMatch() {
		if (!complete) this.getInScheme().complete(this);
		if (this.narrowMatch == null) this.narrowMatch = new LinkedList<LinkedConcept>();
		return this.narrowMatch;
	}

	@XmlTransient
	@iri(SchemeInterface.skosNamespace+"narrowMatch")
	public LinkedList<String> getScheme_AboutNarrowMatch() {
		return LinkedConcept.getScheme_About(this.getNarrowMatch());
	}

	
	/**
	 * @return the broader
	 */
	public LinkedList<LinkedConcept> getRelatedMatch() {
		if (!complete) this.getInScheme().complete(this);
		if (this.relatedMatch == null) this.relatedMatch = new LinkedList<LinkedConcept>();
		return this.relatedMatch;
	}

	@XmlTransient
	@iri(SchemeInterface.skosNamespace+"relatedMatch")
	public LinkedList<String> getScheme_AboutRelatedMatch() {
		return LinkedConcept.getScheme_About(this.getRelatedMatch());
	}

	/**
	 * @return the related
	 */
	public LinkedList<LocalLinkedConcept> getRelated() {
		if (!complete) this.getInScheme().complete(this);
		if (this.related == null) this.related = new LinkedList<LocalLinkedConcept>();
		return this.related;
	}

	@XmlTransient
	@iri(SchemeInterface.skosNamespace+"related")
	public LinkedList<String> getAboutRelated() {
		if (this.related == null) this.related = new LinkedList<LocalLinkedConcept>();
		return LocalLinkedConcept.getAbout(this.getRelated());
	}
	
	/*
	 * Evaluate broaders depth first (no duplicate)
	 */
	private void scheme_aboutBroaderTransitive(LinkedList<String> result, boolean matchAlso) {
		for (LinkedConcept match: getBroader()) {
			Concept aConcept = this.getInScheme().getConcept(match.getAbout());
			if (aConcept != null) {
				aConcept.putNarrower(this.getAbout(),match.getCurrentStatus(),match.getRelationQualifier());
				String realAbout = aConcept.getScheme_About();
				if (!result.contains(realAbout)) {
					result.add(realAbout); // .add is ALWAYS true !!!
					aConcept.scheme_aboutBroaderTransitive(result,matchAlso);
					if (matchAlso) aConcept.scheme_aboutBroadMatchTransitive(result, true);
				}
			}
		}
	}

	/*
	 * Evaluate broadMatches depth first (no duplicate)
	 */
	private void scheme_aboutBroadMatchTransitive(LinkedList<String> result, boolean broaderAlso) {
		SkosManager skosManager = this.getManagerInstance();
		if (skosManager == null) return;
		for (LinkedConcept match: getBroadMatch()) {
			Concept aConcept = skosManager.getScheme_Concept(match.getAbout());
			if (aConcept != null) {
				aConcept.putNarrowMatch(this.getScheme_About(),match.getCurrentStatus(),match.getRelationQualifier());
				String realAbout = aConcept.getScheme_About();
				if (!result.contains(realAbout)) {
					result.add(realAbout); // .add is ALWAYS true !!!
					aConcept.scheme_aboutBroadMatchTransitive(result,broaderAlso);
					if (broaderAlso) aConcept.scheme_aboutBroaderTransitive(result, true);
				}
			}
		}
	}

	/*
	 * Evaluate narrowers depth first (no duplicate)
	 */
	private void scheme_aboutNarrowerTransitive(LinkedList<String> result, boolean matchAlso) {
		for (LocalLinkedConcept match: getNarrower()) {
			Concept aConcept = this.getInScheme().getConcept(match.getAbout());
			if (aConcept != null) {
				aConcept.putBroader(this.getAbout(),match.getCurrentStatus(),match.getRelationQualifier());
				String realAbout = aConcept.getScheme_About();
				if (!result.contains(realAbout)) {
					result.add(realAbout); // .add is ALWAYS true !!!
					aConcept.scheme_aboutNarrowerTransitive(result,matchAlso);
					if (matchAlso) aConcept.scheme_aboutNarrowMatchTransitive(result, true);
				}
			}
		}
	}

	/*
	 * Evaluate narrowMatches depth first (no duplicate)
	 */
	private void scheme_aboutNarrowMatchTransitive(LinkedList<String> result, boolean narrowerAlso) {
		SkosManager skosManager = this.getManagerInstance();
		if (skosManager == null) return;
		for (LinkedConcept match: getNarrowMatch()) {
			Concept aConcept = skosManager.getScheme_Concept(match.getAbout());
			if (aConcept != null) {
				aConcept.putBroadMatch(this.getScheme_About(),match.getCurrentStatus(),match.getRelationQualifier());
				String realAbout = aConcept.getScheme_About();
				if (!result.contains(realAbout)) {
					result.add(realAbout); // .add is ALWAYS true !!!
					aConcept.scheme_aboutNarrowMatchTransitive(result,narrowerAlso);
					if (narrowerAlso) aConcept.scheme_aboutNarrowerTransitive(result, true);
				}
			}
		}
	}

	/*
	 * Evaluate equivalence Matches depth first (no duplicate)
	 */
	private void scheme_aboutMatchTransitive(LinkedList<String> result, boolean closeAlso) {
		SkosManager skosManager = this.getManagerInstance();
		if (skosManager == null) return;
		for (LinkedConcept match: getExactMatch()) {
			Concept aConcept = skosManager.getScheme_Concept(match.getAbout());
			if (aConcept != null) {
				aConcept.putExactMatch(this.getScheme_About(),match.getCurrentStatus(),match.getRelationQualifier());
				String realAbout = aConcept.getScheme_About();
				if (!result.contains(realAbout)) {
					result.add(realAbout); // .add is ALWAYS true !!!
					aConcept.scheme_aboutMatchTransitive(result,closeAlso);
				}
			}
		}
		if (closeAlso) {
			for (LinkedConcept match: getCloseMatch()) {
				Concept aConcept = skosManager.getScheme_Concept(match.getAbout());
				if (aConcept != null) {
					aConcept.putCloseMatch(this.getScheme_About(),match.getCurrentStatus(),match.getRelationQualifier());
					String realAbout = aConcept.getScheme_About();
					if (!result.contains(realAbout)) {
						result.add(realAbout); // .add is ALWAYS true !!!
						aConcept.scheme_aboutMatchTransitive(result,closeAlso);
					}
				}
			}
		}
	}

	/*
	 * Evaluate broaders depth first (no duplicate)
	 */
	private void scheme_aboutRelatedTransitive(LinkedList<String> result, boolean matchAlso) {
		SkosManager skosManager = this.getManagerInstance();
		if (skosManager == null) return;
		for (LinkedConcept match: getRelated()) {
			Concept aConcept = skosManager.getScheme_Concept(match.getAbout());
			if (aConcept != null) {
				aConcept.putRelated(this.getAbout(),match.getCurrentStatus(),match.getRelationQualifier());
				String realAbout = aConcept.getScheme_About();
				if (!result.contains(realAbout)) {
					result.add(realAbout); // .add is ALWAYS true !!!
					aConcept.scheme_aboutRelatedTransitive(result,matchAlso);
					if (matchAlso) aConcept.scheme_aboutRelatedMatchTransitive(result, true);
				}
			}
		}
	}

	/*
	 * Evaluate broadMatches depth first (no duplicate)
	 */
	private void scheme_aboutRelatedMatchTransitive(LinkedList<String> result, boolean localAlso) {
		SkosManager skosManager = this.getManagerInstance();
		if (skosManager == null) return;
		for (LinkedConcept match: getRelatedMatch()) {
			Concept aConcept = skosManager.getScheme_Concept(match.getAbout());
			if (aConcept != null) {
				aConcept.putRelatedMatch(this.getScheme_About(),match.getCurrentStatus(),match.getRelationQualifier());
				String realAbout = aConcept.getScheme_About();
				if (!result.contains(realAbout)) {
					result.add(realAbout); // .add is ALWAYS true !!!
					aConcept.scheme_aboutRelatedMatchTransitive(result,localAlso);
					if (localAlso) aConcept.scheme_aboutRelatedTransitive(result, true);
				}
			}
		}
	}


	/**
	 * @return all exactMatch above
	 */
	@XmlTransient
	public LinkedList<String> getScheme_AboutExactMatchTransitive() {
		String thisAboutFull = this.getScheme_About();
		LinkedList<String> result = new LinkedList<String>();
		result.add(thisAboutFull);
		this.scheme_aboutMatchTransitive(result, false);
		result.remove(thisAboutFull);
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getExactMatchTransitive() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutExactMatchTransitive());
	}

	/**
	 * @return all exactMatch above
	 */
	@XmlTransient
	public LinkedList<String> getScheme_AboutCloseMatchTransitive() {
		String thisAboutFull = this.getScheme_About();
		LinkedList<String> result = new LinkedList<String>();
		result.add(thisAboutFull);
		this.scheme_aboutMatchTransitive(result, true);
		result.remove(thisAboutFull);
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getCloseMatchTransitive() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutCloseMatchTransitive());
	}

	/**
	 * @return all the broaders AND broadMatch above
	 */
	@XmlTransient
	public LinkedList<String> getScheme_AboutBroaderMatchTransitive() {
		String thisAboutFull = this.getScheme_About();
		LinkedList<String> result = new LinkedList<String>();
		result.add(thisAboutFull);
		this.scheme_aboutBroaderTransitive(result, true);
		this.scheme_aboutBroadMatchTransitive(result, true);
		result.remove(thisAboutFull);
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getBroaderMatchTransitive() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutBroaderMatchTransitive());
	}

	/**
	 * @return all the broaders above
	 */
	@XmlTransient
	public LinkedList<String> getScheme_AboutBroaderTransitive() {
		String thisAboutFull = this.getScheme_About();
		LinkedList<String> result = new LinkedList<String>();
		result.add(thisAboutFull);
		this.scheme_aboutBroaderTransitive(result, false);
		result.remove(thisAboutFull);
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getBroaderTransitive() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutBroaderTransitive());
	}

	@XmlTransient
	public LinkedList<String> getScheme_AboutTopConcepts() {
		if (!complete) this.getInScheme().complete(this);
		LinkedList<String> result = new LinkedList<String>();
		if (this.broader == null || this.broader.isEmpty()) {
			result.add(this.getScheme_About());
		} else {
			for (String anAbout: getAboutBroader()) {
				Concept aConcept = this.getInScheme().getConcept(anAbout);
				if (aConcept != null) {
					result.addAll(aConcept.getScheme_AboutTopConcepts());
				}
			}
		}
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getTopConcepts() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutTopConcepts());
	}
	
	/**
	 * @return all the broadMatches above
	 */
	@XmlTransient
	public LinkedList<String> getScheme_AboutBroadMatchTransitive() {
		String thisAboutFull = this.getScheme_About();
		LinkedList<String> result = new LinkedList<String>();
		result.add(thisAboutFull);
		this.scheme_aboutBroadMatchTransitive(result, false);
		result.remove(thisAboutFull);
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getBroadMatchTransitive() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutBroadMatchTransitive());
	}

	/**
	 * @return all the narrowers AND narrowMatch under
	 */
	@XmlTransient
	public LinkedList<String> getScheme_AboutNarrowerMatchTransitive() {
		String thisAboutFull = this.getScheme_About();
		LinkedList<String> result = new LinkedList<String>();
		result.add(thisAboutFull);
		this.scheme_aboutNarrowerTransitive(result, true);
		this.scheme_aboutNarrowMatchTransitive(result, true);
		result.remove(thisAboutFull);
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getNarrowerMatchTransitive() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutNarrowerMatchTransitive());
	}

	/**
	 * @return all the narrowers under
	 */
	@XmlTransient
	public LinkedList<String> getScheme_AboutNarrowerTransitive() {
		String thisAboutFull = this.getScheme_About();
		LinkedList<String> result = new LinkedList<String>();
		result.add(thisAboutFull);
		this.scheme_aboutNarrowerTransitive(result, false);
		result.remove(thisAboutFull);
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getNarrowerTransitive() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutNarrowerTransitive());
	}

	/**
	 * @return all the narrowMatches under
	 */
	@XmlTransient
	public LinkedList<String> getScheme_AboutNarrowMatchTransitive() {
		String thisAboutFull = this.getScheme_About();
		LinkedList<String> result = new LinkedList<String>();
		result.add(thisAboutFull);
		this.scheme_aboutNarrowMatchTransitive(result, false);
		result.remove(thisAboutFull);
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getNarrowMatchTransitive() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutNarrowMatchTransitive());
	}

	/**
	 * @return all the related AND relatedMatch
	 */
	@XmlTransient
	public LinkedList<String> getScheme_AboutAllRelatedTransitive() {
		String thisAboutFull = this.getScheme_About();
		LinkedList<String> result = new LinkedList<String>();
		result.add(thisAboutFull);
		this.scheme_aboutRelatedTransitive(result, true);
		this.scheme_aboutRelatedMatchTransitive(result, true);
		result.remove(thisAboutFull);
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getAllRelatedTransitive() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutAllRelatedTransitive());
	}

	/**
	 * @return all the Related
	 */
	@XmlTransient
	public LinkedList<String> getScheme_AboutRelatedTransitive() {
		String thisAboutFull = this.getScheme_About();
		LinkedList<String> result = new LinkedList<String>();
		result.add(thisAboutFull);
		this.scheme_aboutRelatedTransitive(result, false);
		result.remove(thisAboutFull);
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getRelatedTransitive() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutRelatedTransitive());
	}

	/**
	 * @return all the RelatedMatches
	 */
	@XmlTransient
	public LinkedList<String> getScheme_AboutRelatedMatchTransitive() {
		String thisAboutFull = this.getScheme_About();
		LinkedList<String> result = new LinkedList<String>();
		result.add(thisAboutFull);
		this.scheme_aboutRelatedMatchTransitive(result, false);
		result.remove(thisAboutFull);
		return result;
	}
	@XmlTransient
	public LinkedList<ConceptIdentifier> getRelatedMatchTransitive() {
		return ConceptIdentifier.makeIdentifiers(getScheme_AboutRelatedMatchTransitive());
	}

	/**
	 * @return the internal Note
	 */
	public String getInternalNote() {
		return internalNote;
	}

	/**
	 * @return the inScheme
	 */
	@XmlTransient
	public ConceptScheme getInScheme() {
		return inScheme;
	}

	/**
	 * @return the prefLabel and all altLabel
	 */
	@XmlTransient
	public TermList getLabel() {
		TermList namesGang = new TermList();
		if (this.prefLabel != null)
			namesGang.addAll(this.prefLabel);
		if (this.altLabel != null) {
			for (Term current : this.altLabel) {
				namesGang.addNew(current);
			}
		}
		return namesGang;
	}

	/**
	 * @return the narrower
	 */
	@XmlTransient
	public LinkedList<LocalLinkedConcept> getNarrower() {
		if (!complete) this.getInScheme().complete(this);
		if (this.narrower == null) this.narrower = new LinkedList<LocalLinkedConcept>();
		return this.narrower;
	}

	@XmlTransient
	@iri(SchemeInterface.skosNamespace+"narrower")
	public LinkedList<String> getAboutNarrower() {
		return LocalLinkedConcept.getAbout(this.getNarrower());
	}


	/**
	 * @return the notation
	 */
	public LinkedList<Notation> getNotation() {
		if (!complete) this.getInScheme().complete(this);
		if (this.notation == null) this.notation = new LinkedList<Notation>();
		return this.notation;
	}

	/**
	 * @return the notation
	 */
	public String getNotation(String requestedNotationScheme) {
		for (Notation aDerivation : notation) {
			if (aDerivation.getScheme().equals(requestedNotationScheme)) {
				return aDerivation.getNotation();
			}
		}
		return null; // code not found for the requested list!
	}

	/**
	 * @return the first notation for a NotationScheme
	 */
	public Notation getNotation(NotationScheme requestedNotationScheme) {
		if (!complete) this.getInScheme().complete(this);
		String notationSchemeAbout = requestedNotationScheme.getAbout();
		for (Notation aDerivation : notation) {
			if (aDerivation.getScheme().equals(notationSchemeAbout)) {
				return aDerivation;
			}
		}
		return null; // code not found for the requested list!
	}

    @XmlAttribute
	public long getSortKey() {
		return sortKey;
	}

	public void setSortKey(long sortKey) {
		this.sortKey = sortKey;
	}

	public void mergeSortKey(long sortKey) {
		if (sortKey != 0) this.sortKey = sortKey;
	}

	/**
	 * @return the prefLabel
	 */
	@iri(SchemeInterface.skosNamespace+"prefLabel")
	public TermList getPrefLabel() {
		return prefLabel;
	}

	/**
	 * @param language
	 * @param strictLanguage TODO
	 * @return the best name for current language
	 */
	public String getPrefLabel(String language, boolean strictLanguage) {
		Term aTerm = getPrefLabelTerm(language, strictLanguage);
		if (aTerm == null) return about;
		else {
			return aTerm.toSimpleString();
		}
	}

	/**
	 * @param language
	 * @param strictLanguage TODO
	 * @return the best name for current language
	 */
	public Term getPrefLabelTerm(String language, boolean strictLanguage) {
		if (prefLabel == null) return new Term(TermList.anyLanguage, TermFormat.REGULAR, about, null);
		LinkedList<String> supraLanguage = null;
		if (this.inScheme != null)
			supraLanguage = this.inScheme.getSupraLanguage(); 
		return prefLabel.getLingTerm(supraLanguage,language, strictLanguage);
	}

	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getSortLabel(String language) {
		String sortLabel = Util.formatLong(sortKey);
		if (prefLabel == null) return sortLabel+':'+about;
		LinkedList<String> supraLanguage = null;
		if (this.inScheme != null)
			supraLanguage = this.inScheme.getSupraLanguage(); 
		String label = prefLabel.getLing(supraLanguage,language, false);
		if (label == null) {
			label = "";
		} else {
			label = Util.noAccent(label.toUpperCase());
		}
		return sortLabel+':'+label+' '+about;
	}

	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getSortLabel(String language, boolean alfaOnly) {
		if (!alfaOnly) {
			return getSortLabel(language);
		}
		else {
			if (prefLabel == null) return about;
			LinkedList<String> supraLanguage = null;
			if (this.inScheme != null)
				supraLanguage = this.inScheme.getSupraLanguage(); 
			String label = prefLabel.getLing(supraLanguage,language, false);
			if (label == null) {
				label = "";
			} else {
				label = Util.noAccent(label.toUpperCase());
			}
			return label+' '+about;
		}
	}

	@XmlTransient
	public String getScheme_About() {
		if (inScheme == null) return "null"+SchemeInterface.separator+about;
		return inScheme.getAbout()+SchemeInterface.separator+about;
	}

	/**
	 * @return the scopeNote
	 */
	@iri(SchemeInterface.skosNamespace+"changeNote")
	public TermList getChangeNote() {
		return changeNote;
	}
	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getChangeNote(String language) {
		if (changeNote == null) return null;
		LinkedList<String> supraLanguage = null;
		if (this.inScheme != null)
			supraLanguage = this.inScheme.getSupraLanguage(); 
		return changeNote.getLing(supraLanguage,language, false);
	}

	/**
	 * @return the scopeNote
	 */
	@iri(SchemeInterface.skosNamespace+"definition")
	public TermList getDefinition() {
		return definition;
	}
	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getDefinition(String language) {
		if (definition == null) return null;
		LinkedList<String> supraLanguage = null;
		if (this.inScheme != null)
			supraLanguage = this.inScheme.getSupraLanguage(); 
		return definition.getLing(supraLanguage,language, false);
	}

	/**
	 * @return the scopeNote
	 */
	@iri(SchemeInterface.skosNamespace+"editorialNote")
	public TermList getEditorialNote() {
		return editorialNote;
	}
	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getEditorialNote(String language) {
		if (editorialNote == null) return null;
		LinkedList<String> supraLanguage = null;
		if (this.inScheme != null)
			supraLanguage = this.inScheme.getSupraLanguage(); 
		return editorialNote.getLing(supraLanguage,language, false);
	}

	/**
	 * @return the scopeNote
	 */
	@iri(SchemeInterface.skosNamespace+"example")
	public TermList getExample() {
		return example;
	}
	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getExample(String language) {
		if (example == null) return null;
		LinkedList<String> supraLanguage = null;
		if (this.inScheme != null)
			supraLanguage = this.inScheme.getSupraLanguage(); 
		return example.getLing(supraLanguage,language, false);
	}

	/**
	 * @return the scopeNote
	 */
	@iri(SchemeInterface.skosNamespace+"historyNote")
	public TermList getHistoryNote() {
		return historyNote;
	}
	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getHistoryNote(String language) {
		if (historyNote == null) return null;
		LinkedList<String> supraLanguage = null;
		if (this.inScheme != null)
			supraLanguage = this.inScheme.getSupraLanguage(); 
		return historyNote.getLing(supraLanguage,language, false);
	}

	/**
	 * @return the scopeNote
	 */
	@iri(SchemeInterface.skosNamespace+"scopeNote")
	public TermList getScopeNote() {
		return scopeNote;
	}
	
	public String getScopeNote(String language) {
		Term aTerm = getScopeNoteTerm(language);
		if (aTerm == null) return about;
		else {
			return aTerm.toSimpleString();
		}
	}

	/**
	 * @param language
	 * @return the best name for current language
	 */
	public Term getScopeNoteTerm(String language) {
		if (scopeNote == null) return null;
		LinkedList<String> supraLanguage = null;
		if (this.inScheme != null)
			supraLanguage = this.inScheme.getSupraLanguage(); 
		return scopeNote.getLingTerm(supraLanguage,language, false);
	}

	@XmlTransient
	public ConceptScheme getTopConceptOf() {
		if (this.isTopConcept()) return this.inScheme;
		return null;
	}

	@XmlTransient
	public boolean isTopConcept() {
		return (getBroader().size()==0);
	}

	/**
	 * @return all the narrowerUsage
	 */
	public ReferringApplicationList getNarrowerUsage() {
		if (!complete) this.getInScheme().complete(this);
		if (narrowerUsage == null) narrowerUsage = new ReferringApplicationList();
		return narrowerUsage;
	}

	/**
	 * @return the urls
	 */
	public UrlList getUrl() {
		return urls;
	}
	public String getUrl(String language) {
		if (urls == null) return null;
		return urls.getLing(language);
	}
	public String getUrlAdapted(SkosHtmlManager skosHtmlManager) {
		return skosHtmlManager.getAdaptedUrl(this, urls);
	}
	
	/*
	 * Returns the URI for this concept
	 */
	public String toUriConcept(SkosHtmlManager skosHtmlManager) {
		NoScheme ns = this.getInScheme();
		if (ns == null) return null;
		return ns.toUriNamespace(skosHtmlManager)+this.getAbout();
	}

	/**
	 * @return the icons
	 */
	public UrlList getIcon() {
		return icons;
	}
	public String getIcon(String language) {
		if (icons == null) return null;
		return icons.getLing(language);
	}
	public String getIconAdapted(SkosHtmlManager skosHtmlManager) {
		return skosHtmlManager.getAdaptedUrl(this, icons);
	}


	/**
	 * @param icons the icons to set
	 */
	public void setIcon(UrlList icons) {
		this.icons = icons;
	}
	
	public void mergeIcon(UrlList icons) {
		if (icons == null) return;
		if (this.icons == null) this.icons = new UrlList();
		this.icons.merge(icons);
	}

	public Url putIcon(String language, String anIcon) {
		if (this.icons == null) this.icons = new UrlList();
		return this.icons.setLing(language, anIcon);
	}

	public Url putIcon(Url aIcon) {
		if (this.icons == null) this.icons = new UrlList();
		return this.icons.setLing(aIcon);		
	}

	/**
	 * @return the usages
	 */
	public ReferringApplicationList getUsage() {
	/*
		if (this.inScheme != null && this.inScheme.getClass() == DynamicScheme.class) {
			this.usages = ( (DynamicScheme) this.inScheme ).getConceptUsage(this.about);
			log.debug(this.about+", address="+hashCode()+", "+this.usages.size()+" usages of Dynamic scheme "+this.inScheme.getAbout());
		} else {
	*/
			if (!complete) this.getInScheme().complete(this);
			if (this.usages == null) {
				this.usages = new ReferringApplicationList();
				log.debug(about+", address="+hashCode()+", new usages.");
			} else {
				log.debug(about+", address="+hashCode()+", "+this.usages.size()+" usages.");
			}
	/*	} */
		return this.usages;
	}
	
	public int getCount (String application, String role) {
		ReferringApplicationList allUsages = getUsage();
		int count = 0;
		if (allUsages != null) {
			ReferringApplication applicationUsages = allUsages.getReferringApplication(application);
			if (applicationUsages != null) {
				Count aCount = applicationUsages.getCount(role);
				if (aCount != null) count = aCount.getCount();
			}
		}
		return count;
	}

	/**
	 * @param usages
	 *            for this application
	 */
	public void mergeApplicationUsage(ReferringApplication incUsages) {
		if (incUsages == null || incUsages.getCount() == null || incUsages.getCount().size() == 0) return;
		ReferringApplication appUsages = this.getUsage().getReferringApplication(incUsages.getApplication());
		boolean modified = false;
		if (appUsages == null) {
			ReferringApplicationList theUsages = this.getUsage();
			modified = theUsages.setReferringApplication(incUsages);
		}
		else {
			for (Count aCount : incUsages.getCount()) {
				if (aCount.getCount() == 0) continue;
				modified = true;
				String indexName = aCount.getRole();
				int incDec = aCount.getCount();
				aCount = appUsages.addCount(indexName,appUsages.getCountCount(indexName)+incDec,
									(incDec > 0? aCount.getCategory():appUsages.getCategory(indexName)),
									(incDec > 0? aCount.isPure(): appUsages.isPure(indexName)) );
				CountCacher.store(this,incUsages.getApplication(),aCount);
			}
		}
		if (modified) {
			this.inScheme.setApplicationUsageModified(incUsages.getApplication(),true);
		}
	}

	/**
	 * @param aliasses:
	 *            the aliasses to add in the set
	 */
	public void putAlias(ConceptScheme list, String redirection, Status aStatus) {
		if (redirection == null) return;
		redirection = SkosUtil.normalizeAbout(redirection);
		if (redirection.length()==0) return;
		if(redirection.equals(this.about)) return; // alias of himself? no no!
		putLocalLinkedConcept(this.getAlias(), redirection, aStatus, null);
		if (list != null) list.putAliasValidated(redirection, this.about);
	}

	/**
	 * @param redirection:
	 *            the redirection to add in the set
	 */
	public void putAlias(String redirection, Status aStatus) {
		putAlias(this.getInScheme(),redirection,aStatus);
	}

	/*
	 * Add an alias id to current concept
	 */
	public boolean putAlias(LocalLinkedConcept alias) {
		return putLocalLinkedConcept(this.getAlias(), alias);
	}

	/**
	 * @param redirection
	 *            the redirection to set
	 */
	public void setAlias(LinkedList<LocalLinkedConcept> redirections) {
		this.aliasses = new LinkedList<LocalLinkedConcept>();
		mergeAlias(redirections);
	}

	public void mergeAlias(LinkedList<LocalLinkedConcept> redirections) {
		if (redirections == null) return;
		for (LinkedConcept redirection: redirections) {
			if (redirection != null)
				putAlias(redirection.getAbout(),redirection.getCurrentStatus());
		}
	}

	/**
	 * @param language
	 * @param format TODO
	 * @param altLabel
	 *            the TreeSet of altLabel to set for that language
	 */
	public Term putAltLabel(String language, TermFormat format, String synonym, String source, Status status) {
		if (this.altLabel == null)
			this.altLabel = new TermList();
		Term aTerm = altLabel.setLingDifferent(language,synonym, source);
		aTerm.setCurrentStatus(status);
		aTerm.setCurrentFormat(format);
		this.inScheme.putIndexTerm(aTerm,this);
		return aTerm;
	}

	/**
	 * @param language
	 * @param altLabel
	 *            the TreeSet of altLabel to set for that language
	 */
	public void putAltLabel(TermList langSynonyms) {
		if (this.altLabel == null)
			this.altLabel = new TermList();
		for (Term aTerm: langSynonyms) {
			this.altLabel.addNew(aTerm);
		}
	}

	/*
	 * Add a new term as a concept's synonym
	 */
	public Term putAltLabel(Term aTerm) {
		if (this.altLabel == null)
			this.altLabel = new TermList();
		return this.altLabel.setLingDifferent(aTerm);
	}

	/**
	 * @param language
	 * @param format TODO
	 * @param altLabel
	 *            the TreeSet of altLabel to set for that language
	 */
	public Term putHiddenLabel(String language, TermFormat format, String synonym, String source, Status status) {
		if (this.hiddenLabel == null)
			this.hiddenLabel = new TermList();
		Term aTerm = hiddenLabel.setLingDifferent(language,synonym, source);
		aTerm.setCurrentStatus(status);
		aTerm.setCurrentFormat(format);
		this.inScheme.putIndexTerm(aTerm,this);
		return aTerm;
	}

	/**
	 * @param language
	 * @param altLabel
	 *            the TreeSet of altLabel to set for that language
	 */
	public void putHiddenLabel(TermList langSynonyms) {
		if (this.hiddenLabel == null)
			this.hiddenLabel = new TermList();
		for (Term aTerm: langSynonyms) {
			this.hiddenLabel.addNew(aTerm);
		}
	}

	public Term putHiddenLabel(Term aTerm) {
		if (this.hiddenLabel == null)
			this.hiddenLabel = new TermList();
		return this.hiddenLabel.setLingDifferent(aTerm);
	}

	/**
	 * @param language
	 * @param format TODO
	 * @param prefLabel
	 *            the TreeSet of prefLabel to set for that language
	 */
	public Term putPrefLabel(String language, TermFormat format, String label, String source, Status status) {
		if (this.prefLabel == null)
			this.prefLabel = new TermList();
		Term aTerm = prefLabel.setLing(language,label, source);
		if (aTerm != null) {
			aTerm.setCurrentStatus(status);
			aTerm.setCurrentFormat(format);
			this.inScheme.putIndexTerm(aTerm,this);
		}
		return aTerm;
	}

	/**
	 * putPrefLabel set a whole TermList as the labels of a concept.
	 * Previous Labels are merged for now. Should be forgotten...?
	 */
	public void putPrefLabel(TermList labels) {
		if (this.prefLabel == null)
			this.prefLabel = new TermList();
		this.prefLabel.mergeUnique(labels);
	}
	/*
	 * Add a new term as a concept's name
	 */
	public Term putPrefLabel(Term aTerm) {
		if (this.prefLabel == null)
			this.prefLabel = new TermList();
		return this.prefLabel.setLing(aTerm);
	}


	/**
	 * @param narrowerUsage
	 *            for this application
	 */
	public void putApplicationNarrowerUsage(ReferringApplication aggregatedChildrenUsages) {
		this.getNarrowerUsage().setReferringApplication(aggregatedChildrenUsages);
	}

	/**
	 * @param usages
	 *            for this application
	 */
	public void putApplicationUsage(ReferringApplication newUsages) {
		boolean modified = this.getUsage().setReferringApplication(newUsages);
		if (inScheme != null && modified) {
			inScheme.setApplicationUsageModified(newUsages.getApplication(), true);
		}
	}

	public void putApplicationUsage(String application, String role, int count, String category, boolean pure) {
		ReferringApplication rau = this.getApplicationUsage(application);
		Count aCount = null;
		if (rau == null) {
			if (count > 0) {
				rau = new ReferringApplication(application);
				aCount = rau.addCount(role, count, category, pure);
				if (aCount == null) return;
				this.putApplicationUsage(rau);
			}
		}
		else {
			aCount = rau.addCount(role, count, category, pure);
		}
		CountCacher.store(this,application,aCount);
	}

	public void addApplicationUsage(String application, String role, int count, String category, boolean pure) {
		ReferringApplication rau = this.getApplicationUsage(application);
		Count aCount = null;
		if (rau == null) {
			rau = new ReferringApplication(application);
			aCount = rau.addCount(role, count, category, pure);
			if (aCount == null) return;
			this.putApplicationUsage(rau);
		}
		else {
			aCount = rau.addCount(role, rau.getCountCount(role)+count, category, pure);
		}
		CountCacher.store(this,application,aCount);
	}

	//relation, lang, currFormat, currLabel, currStatus
	public boolean putRelation(String relation,  String subRel, String lang, TermFormat aFormat, String aLabel, Status aStatus) {
		SkosStatement skosStatement = SkosStatement.makeSkosStatement(this,
				relation, subRel, aLabel, lang,
				null, null);
		Value aValue = skosStatement.prepareNewObject(aStatus);
		return skosStatement.putRelation(aValue);
	}

	private boolean putLinkedConcept(LinkedList<LinkedConcept>rels, LinkedConcept toPut) {
		if (toPut == null) return false;
		String about = toPut.getAbout();
		if (about.length()==0) return false;
		if (about.equals(this.about)) return false; // No self relation!
		LinkedConcept aRel = null;
		for (LinkedConcept curRel : rels) {
			if (about.equals(curRel.getAbout())) {
				aRel = curRel;
				break;
			}
		}
		if (aRel == null) {
			rels.add(toPut);
			return true;
		} else {
			String subRel = toPut.getRelationQualifier();
			Status aStatus = toPut.getCurrentStatus();
			if (subRel != null && !subRel.isEmpty()) {
				aRel.setRelationQualifier(subRel);
			}
			if (aStatus != null) {
				aRel.setCurrentStatus(aStatus);
			}
			return false;
		}
	}

	private boolean putLocalLinkedConcept(LinkedList<LocalLinkedConcept>rels, LocalLinkedConcept toPut) {
		if (toPut == null) return false;
		String about = toPut.getAbout();
		if (about.length()==0) return false;
		if (about.equals(this.about)) return false; // No self relation!
		LocalLinkedConcept aRel = null;
		for (LocalLinkedConcept curRel : rels) {
			if (about.equals(curRel.getAbout())) {
				aRel = curRel;
				break;
			}
		}
		if (aRel == null) {
			rels.add(toPut);
			return true;
		} else {
			String subRel = toPut.getRelationQualifier();
			Status aStatus = toPut.getCurrentStatus();
			if (subRel != null && !subRel.isEmpty()) {
				aRel.setRelationQualifier(subRel);
			}
			if (aStatus != null) {
				aRel.setCurrentStatus(aStatus);
			}
			return false;
		}
	}

	private boolean putLinkedConcept(LinkedList<LinkedConcept>rels, String about, Status aStatus, String subRel) {
		if (about == null) return false;
		about = SkosUtil.normalizeAbout(about);
		if (about.length()==0) return false;
		if (about.equals(this.about)) return false; // No self relation!
		LinkedConcept aRel = null;
		for (LinkedConcept curRel : rels) {
			if (about.equals(curRel.getAbout())) {
				aRel = curRel;
				break;
			}
		}
		if (aRel == null) {
			aRel = new LinkedConcept (about,aStatus,subRel);
			rels.add(aRel);
			return true;
		} else {
			if (subRel != null && !subRel.isEmpty()) {
				aRel.setRelationQualifier(subRel);
			}
			if (aStatus != null) {
				aRel.setCurrentStatus(aStatus);
			}
			return false;
		}
	}

	private boolean putLocalLinkedConcept(LinkedList<LocalLinkedConcept>rels, String about, Status aStatus, String subRel) {
		if (about == null) return false;
		about = SkosUtil.normalizeAbout(about);
		if (about.length()==0) return false;
		if (about.equals(this.about)) return false; // No self relation!
		LocalLinkedConcept aRel = null;
		for (LocalLinkedConcept curRel : rels) {
			if (about.equals(curRel.getAbout())) {
				aRel = curRel;
				break;
			}
		}
		if (aRel == null) {
			aRel = new LocalLinkedConcept (about,aStatus,subRel);
			rels.add(aRel);
			return true;
		} else {
			if (subRel != null && !subRel.isEmpty()) {
				aRel.setRelationQualifier(subRel);
			}
			if (aStatus != null) {
				aRel.setCurrentStatus(aStatus);
			}
			return false;
		}
	}
/* UNCOMMENT IF OF ANY USE:
	private LocalLinkedConcept getLocalLinkedConcept(LinkedList<LocalLinkedConcept>rels, String about) {
		if (about == null) return null;
		about = SkosUtil.normalizeAbout(about);
		if (about.length()==0) return null;
		LocalLinkedConcept aRel = null;
		for (LocalLinkedConcept curRel : rels) {
			if (about.equals(curRel.getAbout())) {
				aRel = curRel;
				break;
			}
		}
		return aRel;
	}

	private LinkedConcept getLinkedConcept(LinkedList<LinkedConcept>rels, String about) {
		if (about == null) return null;
		about = SkosUtil.normalizeAbout(about);
		if (about.length()==0) return null;
		LinkedConcept aRel = null;
		for (LinkedConcept curRel : rels) {
			if (about.equals(curRel.getAbout())) {
				aRel = curRel;
				break;
			}
		}
		return aRel;
	}
*/
	/**
	 * @param child:
	 *            the child to add in the set
	 */
	public boolean putExactMatch(String equiv, Status aStatus, String subRel) {
		return putLinkedConcept(this.getExactMatch(),equiv,aStatus,subRel);
	}

	public boolean putExactMatch(LinkedConcept aLinkedConcept) {
		return putLinkedConcept(this.getExactMatch(),aLinkedConcept);
	}

	/**
	 * @param child:
	 *            the child to add in the set
	 */
	public boolean putCloseMatch(String equiv, Status aStatus, String subRel) {
		return putLinkedConcept(this.getCloseMatch(),equiv,aStatus,subRel);
	}

	public boolean putCloseMatch(LinkedConcept aLinkedConcept) {
		return putLinkedConcept(this.getCloseMatch(),aLinkedConcept);
	}

	/**
	 * @param child:
	 *            the child to add in the set
	 */
	public boolean putBroader(String equiv, Status aStatus, String subRel) {
		return putLocalLinkedConcept(this.getBroader(),equiv,aStatus,subRel);
	}

	/**
	 * @param toPut:
	 *            the child to add in the set
	 */
	public boolean putBroader(LocalLinkedConcept toPut) {
		return putLocalLinkedConcept(this.getBroader(),toPut);
	}

	/**
	 * @param child:
	 *            the child to add in the set
	 */
	public boolean putBroadMatch(String equiv, Status aStatus, String subRel) {
		return putLinkedConcept(this.getBroadMatch(),equiv,aStatus,subRel);
	}

	/**
	 * @param toPut:
	 *            the child to add in the set
	 */
	public boolean putBroadMatch(LinkedConcept toPut) {
		return putLinkedConcept(this.getBroadMatch(),toPut);
	}

	/**
	 * @param child:
	 *            the child to add in the set
	 */
	public boolean putRelated(String equiv, Status aStatus, String subRel) {
		return putLocalLinkedConcept(this.getRelated(),equiv,aStatus,subRel);
	}
	public boolean putRelated(LocalLinkedConcept toPut) {
		return putLocalLinkedConcept(this.getRelated(),toPut);
	}


	/**
	 * @param child:
	 *            the child to add in the set
	 */
	public boolean putRelatedMatch(String equiv, Status aStatus, String subRel) {
		return putLinkedConcept(this.getRelatedMatch(),equiv,aStatus,subRel);
	}
	public boolean putRelatedMatch(LinkedConcept toPut) {
		return putLinkedConcept(this.getRelatedMatch(),toPut);
	}

	/**
	 * @param child:
	 *            the child to add in the set
	 */
	public boolean putNarrower(String child, Status aStatus, String subRel) {
		return putLocalLinkedConcept(this.getNarrower(),child,aStatus,subRel);
	}
	public boolean putNarrower(LocalLinkedConcept toPut) {
		return putLocalLinkedConcept(this.getNarrower(),toPut);
	}

	/**
	 * @param child:
	 *            the child to add in the set
	 */
	public boolean putNarrowMatch(String child, Status aStatus, String subRel) {
		return putLinkedConcept(this.getNarrowMatch(),child,aStatus,subRel);
	}
	public boolean putNarrowMatch(LinkedConcept toPut) {
		return putLinkedConcept(this.getNarrowMatch(),toPut);
	}

	/**
	 * @param language
	 * @param titles
	 *            the name to set for that language
	 */
	public void putLabel(String language, TermFormat format, String label, String source, Status status) {
		putPrefLabel(language, format, label, source, status);
	}

	/**
	 * @param language
	 * @param format TODO
	 * @param titles
	 *            the name to set for that language
	 */
	public Term putChangeNote(String language, TermFormat format, String note, Status status) {
		if (this.changeNote == null) this.changeNote = new TermList();
		Term noterm = this.changeNote.setLing(language,note, null);
		if (noterm != null) {
			noterm.setCurrentStatus(status);
			noterm.setCurrentFormat(format);
		}
		return noterm;
	}

	/**
	 * @param language
	 * @param format TODO
	 * @param titles
	 *            the name to set for that language
	 */
	public Term putChangeNote(Term aTerm) {
		if (this.changeNote == null) this.changeNote = new TermList();
		return this.changeNote.setLing(aTerm);
	}

	/**
	 * @param language
	 * @param format TODO
	 * @param titles
	 *            the name to set for that language
	 */
	public Term putDefinition(String language, TermFormat format, String note, Status status) {
		if (this.definition == null) this.definition = new TermList();
		Term noterm = this.definition.setLing(language,note, null);
		if (noterm != null) {
			noterm.setCurrentStatus(status);
			noterm.setCurrentFormat(format);
		}
		return noterm;
	}

	public Term putDefinition(Term aTerm) {
		if (this.definition == null) this.definition = new TermList();
		return this.definition.setLing(aTerm);
	}

	/**
	 * @param language
	 * @param format TODO
	 * @param titles
	 *            the name to set for that language
	 */
	public Term putEditorialNote(String language, TermFormat format, String note, Status status) {
		if (this.editorialNote == null) this.editorialNote = new TermList();
		Term noterm = this.editorialNote.setLing(language,note, null);
		if (noterm != null) {
			noterm.setCurrentStatus(status);
			noterm.setCurrentFormat(format);
		}
		return noterm;
	}

	public Term putEditorialNote(Term aTerm) {
		if (this.editorialNote == null) this.editorialNote = new TermList();
		return this.editorialNote.setLing(aTerm);
	}

	/**
	 * @param language
	 * @param format TODO
	 * @param titles
	 *            the name to set for that language
	 */
	public Term putExample(String language, TermFormat format, String note, Status status) {
		if (this.example == null) this.example = new TermList();
		Term noterm = this.example.setLing(language,note, null);
		if (noterm != null) {
			noterm.setCurrentStatus(status);
			noterm.setCurrentFormat(format);
		}
		return noterm;
	}

	public Term putExample(Term aTerm) {
		if (this.example == null) this.example = new TermList();
		return this.example.setLing(aTerm);
	}

	/**
	 * @param language
	 * @param format TODO
	 * @param titles
	 *            the name to set for that language
	 */
	public Term putHistoryNote(String language, TermFormat format, String note, Status status) {
		if (this.historyNote == null) this.historyNote = new TermList();
		Term noterm = this.historyNote.setLing(language,note, null);
		if (noterm != null) {
			noterm.setCurrentStatus(status);
			noterm.setCurrentFormat(format);
		}
		return noterm;
	}

	public Term putHistoryNote(Term aTerm) {
		if (this.historyNote == null)
			this.historyNote = new TermList();
		return this.historyNote.setLing(aTerm);
	}

	/**
	 * @param language
	 * @param format TODO
	 * @param titles
	 *            the name to set for that language
	 */
	public void putScopeNote(String language, TermFormat format, String note, Status status) {
		if (this.scopeNote == null) this.scopeNote = new TermList();
		Term noterm = this.scopeNote.setLing(language,note, null);
		if (noterm != null) {
			noterm.setCurrentStatus(status);
			noterm.setCurrentFormat(format);
		}
	}

	public Term putScopeNote(Term aTerm) {
		if (this.scopeNote == null)
			this.scopeNote = new TermList();
		return this.scopeNote.setLing(aTerm);
	}

	/**
	 * @param list where the notation appears:
	 * notation: about in this list
	 */
	public boolean putNotation(String list, String derivation) {
		if (this.notation == null)
			this.notation = new LinkedList<Notation>();
		Notation aDerivation = Notation.newValid(list,derivation);
		if (aDerivation == null) return false; // Invalid notation
		if (!this.notation.contains(aDerivation)) { // new notation
			this.notation.add(aDerivation);
			SkosManager skosManager = this.getManagerInstance();
			if (skosManager != null) {
				NotationScheme derivedList = (NotationScheme) skosManager.getScheme(aDerivation.getScheme());
				derivedList.putConcept(aDerivation.getNotation(), this);
			}
			return true;
		}
		return false;
	}

	/*
	 * Add an alias id to current concept
	 */
	public boolean putNotation(Notation notation) {
		return putNotation(notation.getScheme(), notation.getNotation());
	}

	/**
	 * @param code
	 *            the code to set
	 */
	public void setAbout(String code) {
		if (code == null) this.about = null;
		else this.about = SkosUtil.normalizeAbout(code);
	}

	/**
	 * @param names
	 *            the names to set
	 */
	@iri(SchemeInterface.skosNamespace+"prefLabel")
	public void setPrefLabel(TermList names) {
		this.prefLabel = names;
	}

	public void mergePrefLabel(TermList prefs) {
		if (this.prefLabel == null) this.prefLabel = new TermList();
		this.prefLabel.mergeUnique(prefs);
	}

	/**
	 * @param altLabel
	 *            the altLabel to set
	 */
	public void setAltLabel(TermList synonyms) {
		if (synonyms == null) this.altLabel = new TermList();
		else this.altLabel = synonyms;
	}

	public void mergeAltLabel(TermList synonyms) {
		if (this.altLabel == null) this.altLabel = new TermList();
		this.altLabel.mergeDifferents(synonyms);
	}

	@iri(SchemeInterface.skosNamespace+"hiddenLabel")
	public void setHiddenLabel(TermList hiddenLabel) {
		if (hiddenLabel == null) this.hiddenLabel = new TermList();
		else this.hiddenLabel = hiddenLabel;
	}

	public void mergeHiddenLabel(TermList synonyms) {
		if (this.hiddenLabel == null) this.hiddenLabel = new TermList();
		this.hiddenLabel.mergeDifferents(synonyms);
	}

	/**
	 * @param equivs
	 *            the equivs concepts to set
	 */
	public void setExactMatch(LinkedList<LinkedConcept> equivs) {
		this.exactMatch = LinkedConcept.cleanLinks(equivs);
	}
	/**
	 * @param equivs
	 *            the equivs concepts to set
	 */
	@iri(SchemeInterface.skosNamespace+"closeMatch")
	public void setCloseMatch(LinkedList<LinkedConcept> equivs) {
		this.closeMatch = LinkedConcept.cleanLinks(equivs);
	}

	/**
	 * @param broader
	 *            the broader to set
	 */
	@iri(SchemeInterface.skosNamespace+"broader")
	public void setBroader(LinkedList<LocalLinkedConcept> parents) {
		this.broader = LocalLinkedConcept.cleanLocalLinks(parents);
	}

	/**
	 * @param broader
	 *            the broader to set
	 */
	public void setBroadMatch(LinkedList<LinkedConcept> parents) {
		this.broadMatch = LinkedConcept.cleanLinks(parents);
	}

	/**
	 * @param narrower
	 *            the narrower to set
	 */
	public void setNarrowMatch(LinkedList<LinkedConcept> children) {
		this.narrowMatch = LinkedConcept.cleanLinks(children);
	}

	/**
	 * @param equivs
	 *            the equivs concepts to set
	 */
	public void mergeExactMatch(LinkedList<LinkedConcept> equivs) {
		LinkedConcept.mergeLinks(this.exactMatch, equivs);
	}
	/**
	 * @param equivs
	 *            the equivs concepts to merge
	 */
	@iri(SchemeInterface.skosNamespace+"closeMatch")
	public void mergeCloseMatch(LinkedList<LinkedConcept> equivs) {
		LinkedConcept.mergeLinks(this.closeMatch, equivs);
	}

	/**
	 * @param broader
	 *            the broader to merge
	 */
	@iri(SchemeInterface.skosNamespace+"broader")
	public void mergeBroader(LinkedList<LocalLinkedConcept> parents) {
		LocalLinkedConcept.mergeLocalLinks(this.broader, parents);
	}

	/**
	 * @param broader
	 *            the broader to merge
	 */
	public void mergeBroadMatch(LinkedList<LinkedConcept> parents) {
		LinkedConcept.mergeLinks(this.broadMatch, parents);
	}

	/**
	 * @param narrower
	 *            the narrower to merge
	 */
	public void mergeNarrowMatch(LinkedList<LinkedConcept> children) {
		LinkedConcept.mergeLinks(this.narrowMatch, children);
	}

	/**
	 * @param seealso
	 *            the list of related matching concepts
	 */
	@iri(SchemeInterface.skosNamespace+"relatedMatch")
	public void setRelatedMatch(LinkedList<LinkedConcept> seealso) {
		this.relatedMatch = LinkedConcept.cleanLinks(seealso);
	}

	/**
	 * @param narrower
	 *            the narrower to merge
	 */
	public void mergeRelatedMatch(LinkedList<LinkedConcept> children) {
		 LinkedConcept.mergeLinks(this.relatedMatch, children);
	}

	/**
	 * @param narrower
	 *            the narrower to set
	 */
	public void setNarrower(LinkedList<LocalLinkedConcept> children) {
		this.narrower = LocalLinkedConcept.cleanLocalLinks(children);
	}

	public void mergeNarrower(LinkedList<LocalLinkedConcept> children) {
		LocalLinkedConcept.mergeLocalLinks(this.narrower, children);
	}

	/**
	 * @param related the related to set
	 */
	@iri(SchemeInterface.skosNamespace+"related")
	public void setRelated(LinkedList<LocalLinkedConcept> related) {
		this.related = LocalLinkedConcept.cleanLocalLinks(related);
	}

	public void mergeRelated(LinkedList<LocalLinkedConcept> relates) {
		LocalLinkedConcept.mergeLocalLinks(this.related, relates);
	}


	/**
	 * @param internalNote the internalNote to set
	 */
	public void setInternalNote(String note) {
		if (note == null) this.internalNote = null;
		else this.internalNote = note.trim();
	}

	public void mergeInternalNote(String note) {
		if (note != null && !note.isEmpty()) this.internalNote = note.trim();
	}

	/**
	 * @param inScheme the inScheme to set
	 */
	public void setInScheme(ConceptScheme primary) {
		this.inScheme = primary;
	}

	/**
	 * @param notation the notation to set
	 */
	public void setNotation(LinkedList<Notation> notations) {
		this.notation = notations;
		mergeNotation(notations);
	}

	public void mergeNotation(LinkedList<Notation> notations) {
		if (notations != null) {
			SkosManager skosManager = this.getManagerInstance();
			if (skosManager != null) {
				for (Notation aDerivation : notations) {
					NotationScheme derivedList = (NotationScheme) skosManager.getScheme(aDerivation.getScheme());
					if (derivedList != null) {
						derivedList.putConcept(aDerivation.getNotation(), this);
					}
				}
			}
		}
	}

	@iri(SchemeInterface.skosNamespace+"changeNote")
	public void setChangeNote(TermList changeNote) {
		this.changeNote = changeNote;
	}

	@iri(SchemeInterface.skosNamespace+"definition")
	public void setDefinition(TermList definition) {
		this.definition = definition;
	}

	@iri(SchemeInterface.skosNamespace+"editorialNote")
	public void setEditorialNote(TermList editorialNote) {
		this.editorialNote = editorialNote;
	}

	@iri(SchemeInterface.skosNamespace+"example")
	public void setExample(TermList example) {
		this.example = example;
	}

	@iri(SchemeInterface.skosNamespace+"historyNote")
	public void setHistoryNote(TermList historyNote) {
		this.historyNote = historyNote;
	}

	/**
	 * @param scopeNote the scopeNote to set
	 */
	@iri(SchemeInterface.skosNamespace+"scopeNotes")
	public void setScopeNote(TermList complements) {
		this.scopeNote = complements;
	}

	public void mergeChangeNote(TermList changeNote) {
		if (this.changeNote == null) this.changeNote = new TermList();
		this.changeNote.mergeUnique(changeNote);
	}

	public void mergeDefinition(TermList definition) {
		if (this.definition == null) this.definition = new TermList();
		this.definition.mergeUnique(definition);
	}

	public void mergeEditorialNote(TermList editorialNote) {
		if (this.editorialNote == null) this.editorialNote = new TermList();
		this.editorialNote.mergeUnique(editorialNote);
	}

	public void mergeExample(TermList example) {
		if (this.example == null) this.example = new TermList();
		this.example.mergeUnique(example);
	}

	public void mergeHistoryNote(TermList historyNote) {
		if (this.historyNote == null) this.historyNote = new TermList();
		this.historyNote.mergeUnique(historyNote);
	}

	public void mergeScopeNote(TermList complements) {
		if (this.scopeNote == null) this.scopeNote = new TermList();
		this.scopeNote.mergeUnique(complements);
	}

	/**
	 * @param narrowerUsage
	 *            the narrowerUsage to set
	 */
	public void setNarrowerUsage(
			ReferringApplicationList aggregatedChildrenUsages) {
		this.narrowerUsage = aggregatedChildrenUsages;
	}

	/**
	 * @param urls the urls to set
	 */
	public void setUrl(UrlList urls) {
		this.urls = urls;
	}

	public void mergeUrl(UrlList urls) {
		if (urls == null) return;
		if (this.urls == null) this.urls = new UrlList();
		this.urls.merge(urls);
	}

	public Url putUrl(String language, String anUrl) {
		if (this.urls == null) this.urls = new UrlList();
		return this.urls.setLing(language, anUrl);
	}

	public Url putUrl(Url aUrl) {
		if (this.urls == null) this.urls = new UrlList();
		return this.urls.setLing(aUrl);		
	}

	public void setUsage(ReferringApplicationList newUsages) {
		mergeUsage(newUsages, false);
	}
	/**
	 * @param usages
	 *            the usages to set
	 */
	public void mergeUsage(ReferringApplicationList newUsages, boolean merging) {
		// TODO: Pas normal du tout : sans la ligne suivante, �a ne marche pas!!!
		//this.usages = newUsages;
		if (newUsages == null) return;
		/* */
		for (ReferringApplication anAppl : newUsages) {
			String application = anAppl.getApplication();
			this.putApplicationUsage(anAppl);
			if (inScheme != null) {
				for (Count aCount : anAppl.getCount()) {
					SchemeUsage anUsage = inScheme.getApplicationUsage(application,aCount.getRole());
					if (anUsage == null) {
						anUsage = new SchemeUsage(inScheme,application, aCount.getRole());
						inScheme.putSchemeUsage(anUsage);
						//TODO: From where do we get Search, Complete, Preview URL???
						log.debug(anAppl.getApplication()+", role:"+aCount.getRole()+" usage added (scheme="+inScheme.getAbout()+")");
					}
					else anUsage.setTotal(aCount.getCount()); // merging changes nothing to this ?!
				}
			} else if (merging) {
				log.error(this.getAbout()+", usage set for application "+application+"  but no inScheme defined.");
			}
		}
		/**
					SchemeUsage anUse = aScheme.putApplicationRole(anAppl.getApplication(), aCount.getRole());
					if (anUse == null)
						log.error("Usage of application "+anAppl.getApplication()+", role "+aCount.getRole()+ " not recorded in scheme "+aScheme.getAbout());
					else {
						anUse.setCount(anUse.getCount()+aCount.getCount()); // Probablement abusif!
					}
		 **/
	}

	/**
	 * This function returns the presentation of an authority entry in a "short list" (for instance within the display of another record).
	 * @return the HTML presentation of an authority entry
	 */
	public String toHtmlLabel(SkosHtmlManager skosHtmlManager) {
		return toHtmlLabel(skosHtmlManager,true);
	}

	public String getAskosiUrl(SkosHtmlManager skosHtmlManager, String suffix) {
		String askosiRoot = skosHtmlManager.getApplicationRoot(SkosHtmlManager.askosiDisplayApplication);
		if (askosiRoot==null || askosiRoot.isEmpty()) return "";
		return skosHtmlManager.addFraming(Reflector.substituteURL(askosiRoot,this, SkosHtmlManager.askosiDisplayUrl, skosHtmlManager.getLanguage()))+suffix;
	}

	public String toHtmlLabel(SkosHtmlManager skosHtmlManager, boolean iconWanted) {
		String userLanguage = skosHtmlManager.getLanguage();
		String aName = getPrefLabel(userLanguage, false);
		if (aName == null || aName.length() <= 0) {
			aName = this.about;
		} else {
			aName = Util.escapeHTMLEntities(aName);
		}
		if (inScheme == null) return aName;
		String display = inScheme.getDisplayAdapted(skosHtmlManager,this);
		String conceptIcon = "";
		String schemeIcon = "";
		if (iconWanted) {
			conceptIcon = skosHtmlManager.toHtmlImage(getIconAdapted(skosHtmlManager),aName,false);
			schemeIcon = skosHtmlManager.toHtmlImage(inScheme.getIconAdapted(skosHtmlManager),inScheme.getAbout(),false);
		}
		String otherAppIcon = "<img"+skosHtmlManager.askosiIcon("source_application")+" border=\"0\"/>";
		String askosiRoot = skosHtmlManager.getApplicationRoot(SkosHtmlManager.askosiDisplayApplication);
		//String askosiIcon = "";
		boolean isAskosi = false;
		if (askosiRoot != null) {
			//askosiIcon = SkosHtmlManager.toHtmlImage(skosHtmlManager.adaptURL(SkosHtmlManager.askosiDisplayApplication, SkosHtmlManager.askosiConceptIcon),
			//		SkosHtmlManager.askosiDisplayApplication, false);
			if (SkosHtmlManager.askosiDisplayApplication.equals(skosHtmlManager.getApplication())) {
				isAskosi = true;
			}
		}

		String linkName = "<a name=\""+this.about+"\"></a>" ;
		String displayURL = "";
		if (display != null && !display.isEmpty()) {
			displayURL = "<a href=\""
				+display
				+"\" class=\"skosDisplay\" target=\""+inScheme.getApplication()+"_record\">";
		}
		String askosiURL = "";
		if (askosiRoot != null && !askosiRoot.isEmpty()) {
			askosiURL = "<a href=\""
				+ getAskosiUrl(skosHtmlManager, iconWanted? "":"&expanded=yes" )
				+ "\" class=\"skosDisplay\"";
			if(skosHtmlManager.isFramedInAskosi()){
				askosiURL = askosiURL + " target=\""+SkosHtmlManager.askosiDisplayApplication+"_concept\">";
			} else {
				askosiURL = askosiURL + '>';
			}
			if (isAskosi) {
				return
				linkName
				+ (displayURL.isEmpty()
						? schemeIcon
						: (displayURL+schemeIcon+otherAppIcon+ "</a>") )
				+ askosiURL
				+ conceptIcon
				+ aName
				+ "</a>";			
			} else {
				return linkName
				+ displayURL
				+ schemeIcon
				+ conceptIcon
				+ aName
				+ "</a>"
				+ askosiURL
				//+ askosiIcon
				+ otherAppIcon
				+ "</a>";
			}
		} else { // Askosi pas disponible: lier directement
			return linkName
			+ displayURL
			+ schemeIcon
			+ conceptIcon
			+ aName
			+ "</a>";
		}
	}

	public String toHtmlLabelSimple(SkosHtmlManager skosHtmlManager, boolean iconWanted) {
		String userLanguage = skosHtmlManager.getLanguage();
		String aName = getPrefLabel(userLanguage, false);
		if (aName == null || aName.length() <= 0) {
			aName = this.about;
		} else {
			aName = Util.escapeHTMLEntities(aName);
		}
		if (inScheme == null) return aName;
		if (!iconWanted) return aName;
		String conceptIcon = skosHtmlManager.toHtmlImage(getIconAdapted(skosHtmlManager),aName,false);
		String schemeIcon = skosHtmlManager.toHtmlImage(inScheme.getIconAdapted(skosHtmlManager),inScheme.getAbout(),false);
		return schemeIcon
			+ conceptIcon
			+ aName;
	}

	public String toHtmlLabelUsage(SkosHtmlManager skosHtmlManager) {
		String userLanguage = skosHtmlManager.getLanguage(); // Pas parfait dans le contexte de DSpace!
		if (inScheme == null) {
			String aName = getPrefLabel(userLanguage, false);
			if (aName == null || aName.length() <= 0) aName = this.about;
			return Util.escapeHTMLEntities(aName);		
		}
		return this.toHtmlLabel(skosHtmlManager,true)
				+ this.toHtmlConceptUsage(skosHtmlManager);
	}

	public String toHtmlConceptUsage(SkosHtmlManager skosHtmlManager) {
		StringBuffer output = new StringBuffer();
		ReferringApplicationList theUsages = this.getUsage();
		if (theUsages != null) {
			for (ReferringApplication aReference: theUsages) {
				String resultApp = toHtmlConceptUsage(skosHtmlManager,aReference.getApplication(), true);
				if (resultApp != null && !resultApp.isEmpty()) {
					output.append(" ");
					output.append(resultApp);
				}
			}
		}
		return output.toString();
	}

	public String toRdfConceptUsage(SkosHtmlManager skosHtmlManager) {
		StringBuffer output = new StringBuffer();
		ReferringApplicationList theUsages = this.getUsage();
		if (theUsages != null) {
			for (ReferringApplication aReference: theUsages) {
				String resultApp = toRdfConceptUsage(skosHtmlManager,aReference.getApplication(), true);
				if (resultApp != null && !resultApp.isEmpty()) {
					output.append(resultApp);
				}
			}
		}
		return output.toString();
	}

	public String toHtmlConceptUsage(SkosHtmlManager skosHtmlManager, String application, boolean applicationLink) {
		String userLanguage = skosHtmlManager.getLanguage();
		ReferringApplication aReference = this.getApplicationUsage(application);
		if ( aReference != null ) {
			CountList counts = aReference.getCount();
			if ( counts !=null && !counts.isEmpty()) {
				StringBuffer output = new StringBuffer();
				if (applicationLink) {
					output.append("<a href=\"");
					output.append(skosHtmlManager.getApplicationRoot(application));
					output.append("\" target=\"");
					output.append(application);
					output.append("\" class=\"applicationUsage\">");
					output.append(skosHtmlManager.toHtmlApplication(application,this,true));
					output.append("</a>");
				}
				output.append(toHtmlApplicationUsage(skosHtmlManager,userLanguage,application));
				return output.toString();
			}
		}
		return "";
	}

	public String toRdfConceptUsage(SkosHtmlManager skosHtmlManager, String application, boolean applicationLink) {
		ReferringApplication aReference = this.getApplicationUsage(application);
		if ( aReference != null) {
			CountList counts = aReference.getCount();
			if ( counts !=null && !counts.isEmpty()) {
				StringBuffer output = new StringBuffer();
				// For once in a file, the RDF to describe applications should be generated...
				output.append(toRdfApplicationUsage(skosHtmlManager,application));
				return output.toString();
			}
		}
		return "";
	}

	public String toHtmlCount(SkosHtmlManager skosHtmlManager, String application, Count count, boolean categorize) {
		if (count == null) return "";
		int counter = count.getCount();
		if (counter <= 0) return "";
		if (inScheme == null) return ""+counter;
		SkosManager skosManager =this.getManagerInstance();
		String category ="";
		if (categorize) {
			category = count.getCategory();
			category = skosHtmlManager.toHtmlCategory(application, category, count.isPure(),this);
		}
		String searchURL = null;
		Notation currNotation = null;
		SchemeUsage su = inScheme.getApplicationUsage(application, count.getRole() );
		if (su != null) {
			searchURL = su.getSearchURL();
			NoScheme notationScheme = su.getInScheme();
			if (notationScheme != null && !notationScheme.equals(this.getInScheme()) && notationScheme instanceof NotationScheme) {
				currNotation = this.getNotation((NotationScheme)notationScheme);
			}
		}
		if (searchURL == null) {
			ConceptScheme rolesScheme = skosManager.getRoles();
			if (rolesScheme != null) {
				Concept curRole = rolesScheme.getConcept(count.getRole());
				if (curRole != null) {
					UrlList URLs = curRole.getUrl();
					if (URLs != null) {
						searchURL = URLs.getLing(skosHtmlManager.getLanguage());
					} else return ""+counter+category;
				} else return ""+counter+category;
			} else return ""+counter+category;
		}
		String applicationRoot = skosHtmlManager.getApplicationRoot(application);
		StringBuffer output = new StringBuffer();
		Object[] beans = new Object[3];
		beans[0] = currNotation;
		beans[1] = this;
		beans[2] = count;
		output.append( "<a href=\"");
		output.append(skosHtmlManager.addFraming(Reflector.substituteURL(applicationRoot,beans,searchURL,skosHtmlManager.getLanguage())));
		output.append("\"");
		if (SkosHtmlManager.askosiDisplayApplication.equals(application)) {
			if(skosHtmlManager.isFramedInAskosi()){
				output.append(" target=\"");
				output.append(SkosHtmlManager.askosiDisplayApplication);
				output.append("_scheme\"");
			}
		} else {
			output.append(" target=\"");
			output.append(application);
			output.append("_list\"");
		}
		output.append(" class=\"authorityReferenceTotal\">");
		output.append(counter);
		output.append(category);
		output.append("</a>");
		return output.toString();
	}

	private String stars(int count) {
        StringBuffer newString = new StringBuffer();

        for (int i = 0; i < count; i++)
        {
            newString.append("*");
        }

        return newString.toString();        
    }

	public String toTrace() {
		String schemeCode = "";
		ConceptScheme aScheme = this.getInScheme();
		if (aScheme != null) schemeCode = aScheme.getAbout();
		String aName = "no names";
		LinkedList<String> supraLanguage = null;
		if (this.inScheme != null)
			supraLanguage = this.inScheme.getSupraLanguage(); 
		if (prefLabel != null) {
			aName = prefLabel.getLing(supraLanguage,TermList.anyLanguage, false);
			if (aName != null)
				aName = aName + " x " + prefLabel.size();
			else
				aName = "empty";
		}
		return "[" + stars(depth) + schemeCode + about + "=" + aName + "]";
	}

	/**
	 * @return the collection
	 */
	@XmlTransient
	public LinkedList<CollectionScheme> getCollectionScheme() {
		return collections;
	}

	/**
	 * @param redirection:
	 *            the redirection to add in the set
	 */
	public void putCollectionScheme(CollectionScheme collection) {
		if (this.collections == null)
			this.collections = new LinkedList<CollectionScheme>();
		if (!this.collections.contains(collection)) {
			this.collections.add(collection);
		}
	}

	/**
	 * @param collection the collection to set
	 */
	public void setCollectionScheme(LinkedList<CollectionScheme> collection) {
		this.collections = collection;
	}

	/**
	 * @return the collection
	 */
	public LinkedList<String> getCollection() {
		LinkedList<String> collectionAbout = new LinkedList<String>();
		if (collections != null) {
			for (CollectionScheme aColl : collections) {
				collectionAbout.add(aColl.getAbout());
			}
		}
		return collectionAbout;
	}

	/**
	 * @param redirection:
	 *            the redirection to add in the set
	 */
	public void putCollection(String collectionAbout) {
		SkosManager skosManager = this.getManagerInstance();
		if (skosManager !=  null) {
			SchemeInterface collection = skosManager.getScheme(collectionAbout);
			if (collection instanceof CollectionScheme) putCollectionScheme((CollectionScheme)collection);
		}
	}

	/**
	 * @param collection the collection to set
	 */
	public void setCollection(LinkedList<String> collectionAbout) {
		for (String aCollection : collectionAbout) {
			putCollection(aCollection);
		}
	}

	/**
		�	For current application:
		o	If only one index:
		<a href=�URLsearch[CODEentry]� class=�authorityReferenceCount><img src=�ICONreference� border=0 alt=�NAMEindex� class=�authorityReferenceIcon�/>COUNTreference</a><a href=�URLfilter[currentQuery,CODEentry]� class=�authorityReferenceFilter�/>
		o	If multiple indexes:
		<a href=�URLsearch[CODEentry]� class=�authorityReferenceTotal>COUNTtotal</a><a href=�URLfilter[currentQuery,CODEentry]� class=�authorityReferenceFilter�/>

		For each index:
		<a href=�URLsearch[CODEentry]�><img src=�ICONindex� border=0 alt=�NAMEindex�/>COUNTindex </a><a href=�URLfilter[currentQuery,CODEentry]� class=�authorityReferenceFilter�/>

		�	For other applications, idem but each application information is prefixed by <img src=�ICONapplication� border=0 alt=�NAMEapplication� class=�authorityReferenceApplication�/>
	**/
	
	public int compareTo(Concept other) {
		return this.getScheme_About().compareTo( other.getScheme_About() );
	}

	/**
	 * @return the depth
	 */
	@XmlTransient
	public int getDepth() {
		return depth;
	}

	/**
	 * @param depth the depth to set
	 */
	public void setDepth(int depth) {
		this.depth = depth;
	}
	
	/**
	 * @return the depth
	 */
	@XmlTransient
	public SkosManager getManagerInstance() {
		if (getInScheme() == null) return null;
		return getInScheme().getManagerInstance();
	}

/*
 * Cleans up a concept unmarshalled by XML
 */
	public void cleanUp() {
		setExactMatch(getExactMatch());
		setCloseMatch(getCloseMatch());
		setBroader(getBroader());
		setBroadMatch(getBroadMatch());
		setRelated(getRelated());
		setRelatedMatch(getRelatedMatch());
	}


	public void reloadConcept(Concept source) {
		log.debug("Reload "+about+", address="+hashCode()+" from "+source.about+", address="+source.hashCode());
		setSortKey(source.getSortKey());
		setAlias(source.getAlias());
		setNotation(source.getNotation());
		setPrefLabel(source.getPrefLabel());
		setAltLabel(source.getAltLabel());
		setHiddenLabel(source.getHiddenLabel());
		setChangeNote(source.getChangeNote());
		setDefinition(source.getDefinition());
		setEditorialNote(source.getEditorialNote());
		setExample(source.getExample());
		setHistoryNote(source.getHistoryNote());
		setScopeNote(source.getScopeNote());
		setInternalNote(source.getInternalNote());
		setExactMatch(source.getExactMatch());
		setCloseMatch(source.getCloseMatch());
		setBroader(source.getBroader());
		setBroadMatch(source.getBroadMatch());
		setNarrower(source.getNarrower()); // Necessary to ensure that old inferred relations are erased
		setNarrowMatch(source.getNarrowMatch()); // Necessary to ensure that old inferred relations are erased
		setRelated(source.getRelated());
		setRelatedMatch(source.getRelatedMatch());
		setUsage(source.getUsage());		
		setIcon(source.getIcon());		
		setUrl(source.getUrl());		
		setStatus(source.getStatus());		
	}

	public void mergeImporting(Concept source) {
		log.debug("Merge in "+about+", address="+hashCode()+" from "+source.about+", address="+source.hashCode());
		mergeSortKey(source.getSortKey());
		mergeAlias(source.getAlias());
		mergeNotation(source.getNotation());
		mergePrefLabel(source.getPrefLabel());
		mergeAltLabel(source.getAltLabel());
		mergeHiddenLabel(source.getHiddenLabel());
		mergeChangeNote(source.getChangeNote());
		mergeDefinition(source.getDefinition());
		mergeEditorialNote(source.getEditorialNote());
		mergeExample(source.getExample());
		mergeHistoryNote(source.getHistoryNote());
		mergeScopeNote(source.getScopeNote());
		mergeInternalNote(source.getInternalNote());
		mergeExactMatch(source.getExactMatch());
		mergeCloseMatch(source.getCloseMatch());
		mergeBroader(source.getBroader());
		mergeBroadMatch(source.getBroadMatch());
		mergeNarrower(source.getNarrower()); // Necessary to ensure that old inferred relations are erased
		mergeNarrowMatch(source.getNarrowMatch()); // Necessary to ensure that old inferred relations are erased
		mergeRelated(source.getRelated());
		mergeRelatedMatch(source.getRelatedMatch());
		mergeUsage(source.getUsage(), true);		
		mergeIcon(source.getIcon());		
		mergeUrl(source.getUrl());		
		setCurrentStatus(source.getCurrentStatus());		
	}

	/**
	 * @return the errors
	 */
	@XmlTransient
	public LocalError getErrors() {
		if (this.errors == null) this.errors = new LocalError();
		return this.errors;
	}

	public String toHtmlErrors(SkosHtmlManager skosHtmlManager) {
		if (this.errors == null) return "";
		if (this.errors.size()==0) return "";
		Object[] beans = new Object[2];
		beans[0] = this ;
		beans[1] = this.inScheme ;
		StringBuffer result = new StringBuffer();
		result.append("<ul class=\"LocalError\">");
		for (String message : this.errors) {
			result.append("<li>");
			result.append(
					skosHtmlManager.toHtmlScheme_Concept
						   (Reflector.substituteURL(skosHtmlManager.getApplicationRoot(),beans,message,skosHtmlManager.getLanguage()),
							this.inScheme));
			result.append("</li>");
		}
		result.append("</ul>");
		return result.toString();
	}

	public String toHtmlErrorsTable(SkosHtmlManager skosHtmlManager) {
		if (this.errors == null) return "";
		if (this.errors.size()==0) return "";
		Object[] beans = new Object[2];
		beans[0] = this ;
		beans[1] = this.inScheme ;
		StringBuffer result = new StringBuffer();
		for (String message : this.errors) {
			result.append("<tr><th>");
			result.append(getScheme_About());
			result.append("</th><td>");
			result.append(Reflector.substituteURL(skosHtmlManager.getApplicationRoot(),beans,message,skosHtmlManager.getLanguage()));
			result.append("</td></tr>");
		}
		return result.toString();
	}

	/**
	 * @param skosHtmlManager
	 * 
	 * This procedure will evolve toward providing workflow information (change history) about the status of the concept.
	 * 
	 * @return the image corresponding to the current status of the concept.
	 */
	public String toHtmlStatus(SkosHtmlManager skosHtmlManager) {
		if (this.status == null) return "";
		//if (this.status.length()==0) return "";
		Concept aStatus = skosHtmlManager.getSkosManager().getScheme_Concept(this.status.getScheme_About());
		String statusCode;
		if (aStatus != null) {
			String language = skosHtmlManager.getLanguage();
			statusCode = aStatus.getIconAdapted(skosHtmlManager);
			if (statusCode != null && !statusCode.isEmpty()) {
				statusCode = skosHtmlManager.toHtmlImage(statusCode,aStatus.getAbout(),false);
			} else {
				statusCode = aStatus.getPrefLabel(language, false);
			}
		} else {
			statusCode = this.status.toString();
		}
		return statusCode;
	}

	/**
	 * @param skosHtmlManager
	 * 
	 * The generated HTML assumes the JavaScript methods to manage the client side Basket are accessible.
	 * Current implementation is in tablescroll.js.
	 * 
	 * There is a link which calls togglePrintBasket when the image is clicked.
	 * This link is around an image without source at the beginning.
	 * There is a script snippet to load the right initial image depending on the content of the Basket.
	 * 
	 * @return the "active" image to indicate if a concept is in the Basket or not.
	 */
	public String toHtmlBasket(SkosHtmlManager skosHtmlManager) {
		String about = this.getScheme_About();
		String basket = "<a onclick=\"javascript:skosBasketToggle('"
						+about
						+"')\"><img name=\"skosBasket"
						+about
						+"\"/></a><script type=\"text/javascript\">skosBasketLoad(\""
						+about
						+"\")</script>";
		return basket;
	}

	/**
	 * @param errors the errors to set
	 */
	public void setErrors(LocalError errors) {
		this.errors = errors;
	}

	public void addError(String message) {
		log.error(message);
		this.getErrors().add(message);
		if (this.inScheme != null) this.inScheme.getInError().add(this.about);
	}

	public int checkDuplicatedLabel(TreeMap<String,String> allLabels,boolean languageSensitive) {
		int total = 0;
		TermList termList = this.getPrefLabel();
		if (termList != null) {
			for (Term aTerm: this.getPrefLabel()) {
				total += aTerm.checkDuplicatedLabel(allLabels,languageSensitive,this,"prefLabel");
			}
		}
		termList = this.getAltLabel();
		if (termList != null) {
			for (Term aTerm: termList) {
				total += aTerm.checkDuplicatedLabel(allLabels,languageSensitive,this,"altLabel");
			}
		}
		return total;
	}

	public String toString() {
		return getNamespace()+getScheme_About();
	}

	public String stringValue() {
		return getNamespace()+getScheme_About();
	}

	public String getNamespace() {
		return SESAME_NAMESPACE;
	}
	
	public String getLocalName() {
		return getScheme_About();
	}
	
	public boolean equals(Object o) {
		if (o == null) return false;
		if (this == o) return true;
		if ( ! (o instanceof Concept)) return false;
		return this.getScheme_About().equals(((Concept)o).getScheme_About());
	}
	
	public int hashCode() {
		String identifier = this.getScheme_About();
		if (identifier == null) return 0;
		return this.getScheme_About().hashCode();
	}

	public String getStatus() {
		if (status == null) return null;
		return status.toString();
	}

	public void setStatus(String aStatus) {
		if (aStatus == null || aStatus.isEmpty()) {
			this.status = null;
			return;
		}
		if (aStatus.startsWith("status_")) {
			aStatus = aStatus.substring("status_".length());
		}
		this.status = Status.valueOf(aStatus);
	}

	@XmlTransient
	public Status getCurrentStatus() {
		return status;
	}

	public void setCurrentStatus(Status status) {
		if (status == null) return;
		this.status = status;
	}
	
	public String toRdfObjectUrl() {
		return "&concept="+this.getScheme_About();
	}

	public String toJavaScriptParameters() {
		return ",concept:'"+this.getScheme_About()+"'";
	}

	/**
	 * @return the complete
	 */	
	@XmlTransient
	public boolean isComplete() {
		return complete;
	}

	/**
	 * @param complete the complete to set
	 */
	public void setComplete(boolean complete) {
		this.complete = complete;
	}
	
}
